/**
 * ChatParser 属性测试
 * 
 * 使用 fast-check 进行属性测试，验证聊天解析器的正确性
 */

import { describe, it, expect } from 'vitest';
import * as fc from 'fast-check';
import { ChatParser } from './ChatParser';
import type { ChatMessage, ParsedChat, EmotionalTone } from '../types';

// 创建解析器实例
const parser = new ChatParser();

// ============================================
// 生成器定义
// ============================================

/**
 * 生成有效的发送者名称
 * 限制长度在 1-15 个字符，不包含特殊分隔符
 */
const senderArbitrary = fc.string({ minLength: 1, maxLength: 15 })
  .filter(s => {
    const trimmed = s.trim();
    // 排除空白字符串、包含冒号或方括号的字符串
    return trimmed.length > 0 && 
           !trimmed.includes(':') && 
           !trimmed.includes('：') &&
           !trimmed.includes('[') &&
           !trimmed.includes(']') &&
           !trimmed.includes('\n');
  })
  .map(s => s.trim());

/**
 * 生成有效的消息内容
 * 不包含换行符，长度在 1-100 个字符
 */
const contentArbitrary = fc.string({ minLength: 1, maxLength: 100 })
  .filter(s => {
    const trimmed = s.trim();
    return trimmed.length > 0 && !trimmed.includes('\n');
  })
  .map(s => s.trim());

/**
 * 生成情感基调
 */
const emotionalToneArbitrary: fc.Arbitrary<EmotionalTone> = fc.constantFrom(
  'happy', 'sad', 'comfort', 'funny', 'grateful', 'apologetic', 'neutral'
);

/**
 * 生成单条聊天消息
 * 注意：此生成器用于类型定义参考，实际测试使用 multiSenderMessagesArbitrary
 */
const _chatMessageArbitrary: fc.Arbitrary<ChatMessage> = fc.record({
  sender: senderArbitrary,
  content: contentArbitrary,
  isUser: fc.boolean()
});
// 导出以避免未使用警告
void _chatMessageArbitrary;

/**
 * 生成多条聊天消息（至少2条，来自不同发送者）
 */
const multiSenderMessagesArbitrary = fc.tuple(
  fc.array(senderArbitrary, { minLength: 2, maxLength: 5 }),
  fc.array(contentArbitrary, { minLength: 2, maxLength: 10 })
).chain(([senders, contents]) => {
  // 确保有不同的发送者
  const uniqueSenders = [...new Set(senders)];
  if (uniqueSenders.length < 2) {
    uniqueSenders.push(uniqueSenders[0] + '_other');
  }
  
  // 为每条内容随机分配发送者
  return fc.array(
    fc.integer({ min: 0, max: uniqueSenders.length - 1 }),
    { minLength: contents.length, maxLength: contents.length }
  ).map(senderIndices => {
    return contents.map((content, i) => ({
      sender: uniqueSenders[senderIndices[i]],
      content,
      isUser: senderIndices[i] === 1 // 第二个发送者是用户
    }));
  });
});


/**
 * 生成有效的 ParsedChat 对象
 */
const parsedChatArbitrary: fc.Arbitrary<ParsedChat> = multiSenderMessagesArbitrary
  .chain(messages => {
    const participants = [...new Set(messages.map(m => m.sender))];
    return emotionalToneArbitrary.map(emotionalTone => ({
      messages,
      participants,
      emotionalTone
    }));
  });

/**
 * 生成仅包含空白字符的字符串
 */
const whitespaceOnlyArbitrary = fc.stringOf(
  fc.constantFrom(' ', '\t', '\n', '\r'),
  { minLength: 0, maxLength: 20 }
);

// ============================================
// Property 6: 聊天解析正确性
// ============================================

describe('ChatParser Property Tests', () => {
  /**
   * **Feature: smart-chat-reply, Property 6: 聊天解析正确性**
   * **Validates: Requirements 5.1, 5.2**
   * 
   * 对于任意包含多个发言者的聊天记录文本，ChatParser.parse() 应：
   * - 正确识别所有不同的发言者（participants 数组长度与实际发言者数量一致）
   * - 保持消息的原始顺序（messages 数组顺序与输入顺序一致）
   */
  describe('Property 6: 聊天解析正确性', () => {
    it('应正确识别所有发言者并保持消息顺序', () => {
      fc.assert(
        fc.property(multiSenderMessagesArbitrary, (messages) => {
          // 构造标准格式的聊天文本
          const rawText = messages
            .map(msg => `${msg.sender}: ${msg.content}`)
            .join('\n');
          
          // 解析
          const result = parser.parse(rawText);
          
          // 验证解析成功
          expect(result.success).toBe(true);
          expect(result.data).toBeDefined();
          
          const parsed = result.data!;
          
          // 验证消息数量一致
          expect(parsed.messages.length).toBe(messages.length);
          
          // 验证消息顺序和内容一致
          for (let i = 0; i < messages.length; i++) {
            expect(parsed.messages[i].sender).toBe(messages[i].sender);
            expect(parsed.messages[i].content).toBe(messages[i].content);
          }
          
          // 验证参与者数量正确
          const expectedParticipants = new Set(messages.map(m => m.sender));
          expect(parsed.participants.length).toBe(expectedParticipants.size);
          
          // 验证所有参与者都被识别
          for (const participant of expectedParticipants) {
            expect(parsed.participants).toContain(participant);
          }
        }),
        { numRuns: 100 }
      );
    });
  });


  // ============================================
  // Property 7: 聊天格式化往返一致性
  // ============================================

  /**
   * **Feature: smart-chat-reply, Property 7: 聊天格式化往返一致性**
   * **Validates: Requirements 5.4**
   * 
   * 对于任意有效的 ParsedChat 对象，执行 parse(format(chat)) 应产生与原始 chat 等价的结果
   * （消息内容和发言者信息一致）
   */
  describe('Property 7: 聊天格式化往返一致性', () => {
    it('format 后再 parse 应产生等价结果', () => {
      fc.assert(
        fc.property(parsedChatArbitrary, (originalChat) => {
          // 格式化
          const formatted = parser.format(originalChat);
          
          // 重新解析
          const result = parser.parse(formatted);
          
          // 验证解析成功
          expect(result.success).toBe(true);
          expect(result.data).toBeDefined();
          
          const reparsed = result.data!;
          
          // 验证消息数量一致
          expect(reparsed.messages.length).toBe(originalChat.messages.length);
          
          // 验证消息内容和发送者一致
          for (let i = 0; i < originalChat.messages.length; i++) {
            expect(reparsed.messages[i].sender).toBe(originalChat.messages[i].sender);
            expect(reparsed.messages[i].content).toBe(originalChat.messages[i].content);
          }
          
          // 验证参与者一致（使用 Set 比较，忽略顺序）
          const originalParticipants = new Set(originalChat.participants);
          const reparsedParticipants = new Set(reparsed.participants);
          
          expect(reparsedParticipants.size).toBe(originalParticipants.size);
          for (const p of originalParticipants) {
            expect(reparsedParticipants.has(p)).toBe(true);
          }
        }),
        { numRuns: 100 }
      );
    });
  });

  // ============================================
  // Property 8: 空白输入错误处理
  // ============================================

  /**
   * **Feature: smart-chat-reply, Property 8: 空白输入错误处理**
   * **Validates: Requirements 7.1**
   * 
   * 对于任意仅包含空白字符（空格、制表符、换行符）的字符串输入，
   * ChatParser.parse() 应返回错误结果而非抛出异常
   */
  describe('Property 8: 空白输入错误处理', () => {
    it('空白输入应返回错误而非抛出异常', () => {
      fc.assert(
        fc.property(whitespaceOnlyArbitrary, (whitespaceInput) => {
          // 不应抛出异常
          let result;
          expect(() => {
            result = parser.parse(whitespaceInput);
          }).not.toThrow();
          
          // 应返回失败结果
          expect(result).toBeDefined();
          expect(result!.success).toBe(false);
          expect(result!.error).toBeDefined();
          expect(typeof result!.error).toBe('string');
          expect(result!.error!.length).toBeGreaterThan(0);
        }),
        { numRuns: 100 }
      );
    });

    it('空字符串应返回错误', () => {
      const result = parser.parse('');
      expect(result.success).toBe(false);
      expect(result.error).toBe('请输入聊天记录');
    });
  });
});
